package com.daqian.chance.redis.conf;

import org.springframework.data.redis.core.*;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 参考：https://www.cnblogs.com/superfj/p/9232482.html
 */
@Component
public class RedisService {

    /**
     * 默认过期时长，单位：秒
     */
    public static final long DEFAULT_EXPIRE = 60 * 60 * 24;

    /**
     * 不设置过期时长
     */
    public static final long NOT_EXPIRE = -1;

    private RedisTemplate<String, Object> redisTemplate;

    private ValueOperations<String, Object> opsValue;

    private HashOperations<String, String, Object> opsHash;

    private ListOperations<String, Object> opsList;

    private SetOperations<String, Object> opsSet;

    private ZSetOperations<String, Object> opsZSet;

    public RedisService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.opsValue = redisTemplate.opsForValue();
        this.opsHash = redisTemplate.opsForHash();
        this.opsList = redisTemplate.opsForList();
        this.opsSet = redisTemplate.opsForSet();
        this.opsZSet = redisTemplate.opsForZSet();
    }

    /**
     * 返回自增值
     * @param key
     * @param liveTime 秒
     * @return
     */
    public Long incr(String key, long liveTime) {
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
        Long increment = entityIdCounter.getAndIncrement();
        if ((null == increment || increment.longValue() == 0) && liveTime > 0) {
            entityIdCounter.expire(liveTime, TimeUnit.SECONDS);
        }
        return increment;
    }

    /**
     * 设置value（默认1天后失效）
     * @param key
     * @param value
     */
    public void setValue(String key, String value) {
        this.setValue(key, value, DEFAULT_EXPIRE);
    }

    /**
     * 设置value（sec秒后过期）
     * @param key
     * @param value
     * @param sec
     */
    public void setValue(String key, Object value, long sec) {
        this.opsValue.set(key, value, sec, TimeUnit.SECONDS);
    }

    /**
     * 根据key获取value
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getValue(String key) {
        return (T) this.opsValue.get(key);
    }

    /**
     * 为散列添加或者覆盖一个 key-value键值对
     * @param key
     * @param hashKey
     * @param value
     */
    public void hashPut(String key, String hashKey, Object value) {
        this.opsHash.put(key, hashKey, value);
    }

    /**
     * 为散列添加多个key-value键值对
     * @param key
     * @param map
     */
    public void putAll(String key, Map<? extends String, ? extends Object> map) {
        this.opsHash.putAll(key, map);
    }

    /**
     * 获取key中hash散列中的值
     * @param key
     * @param hashKey
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T hashGet(String key, String hashKey) {
        return (T) this.opsHash.get(key, hashKey);
    }

    /**
     * 获取散列的value集合
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> HashValues(String key) {
        return (List<T>) this.opsHash.values(key);
    }

    /**
     * 得到多个key的值。
     * @param key
     * @param hashKeys
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> hashMultiGet(String key, Collection<String> hashKeys) {
        return (List<T>) this.opsHash.multiGet(key, hashKeys);
    }

    /**
     * 获取散列的key-value键值对集合
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Map<String, T> HashEntries(String key) {
        return (Map<String, T>) this.opsHash.entries(key);
    }

    /**
     * 为散了中某个值加上 整型 delta
     * @param key
     * @param hashKey
     * @param delta
     * @return
     */
    public Long hashIncrement(String key, String hashKey, long delta) {
        return this.opsHash.increment(key, hashKey, delta);
    }

    /**
     * 为散了中某个值加上 double delta
     * @param key
     * @param hashKey
     * @param delta
     * @return
     */
    public Double hashIncrement(String key, String hashKey, double delta) {
        return this.opsHash.increment(key, hashKey, delta);
    }


    /**
     * 返回列表中指定区间内的元素，区间以偏移量 START 和 END 指定。
     * (下标 0 表示列表的第一个元素， 1 表示列表的第二个元素，以此类推。你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。)
     * @param key
     * @param start 开始下标
     * @param end   结束下标
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> listRange(String key, long start, long end) {
        return (List<T>) this.opsList.range(key, start, end);
    }

    /**
     * 让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除
     * (下标 0 表示列表的第一个元素， 1 表示列表的第二个元素，以此类推。你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。)
     * @param key
     * @param start
     * @param end
     */
    public void listTrim(String key, long start, long end) {
        this.opsList.trim(key, start, end);
    }

    /**
     * 是左面进入 也就是靠前 先进后出
     * @param key
     * @param value
     */
    public void listLeftPush(String key, Object value) {
        this.opsList.leftPush(key, value);
    }

    /**
     * 多个值同 listLeftPush
     * @param key
     * @param values
     */
    public void listLeftPushAll(String key, Collection<Object> values) {
        this.opsList.leftPushAll(key, values);
    }

    /**
     * 多个值同 listLeftPush
     * @param key
     * @param values
     */
    public void listLeftPushAll(String key, Object... values) {
        this.opsList.leftPushAll(key, values);
    }


    /**
     * 顺序执行，先进先出
     * @param key
     * @param value
     */
    public void listRightPush(String key, Object value) {
        this.opsList.rightPush(key, value);
    }

    /**
     * 顺序执行，先进先出同listRightPush
     * @param key
     * @param values
     */
    public void listRightPushAll(String key, Collection<Object> values) {
        this.opsList.leftPushAll(key, values);
    }

    /**
     * 顺序执行，先进先出同listRightPush
     * @param key
     * @param values
     */
    public void listRightPushAll(String key, Object... values) {
        this.opsList.leftPushAll(key, values);
    }

    /**
     * 删除元素
     * count > 0 : 从表头开始向表尾搜索，移除与 VALUE 相等的元素，数量为 COUNT 。
     * count < 0 : 从表尾开始向表头搜索，移除与 VALUE 相等的元素，数量为 COUNT 的绝对值。
     * count = 0 : 移除表中所有与 VALUE 相等的值。
     * @param key
     * @param count
     * @param value
     */
    public void listRemove(String key, long count, Object value) {
        this.opsList.remove(key, count, value);
    }

    /**
     * 命令用于通过索引获取列表中的元素。你也可以使用负数下标
     * (下标 0 表示列表的第一个元素， 1 表示列表的第二个元素，以此类推。你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。)
     * @param key
     * @param index
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T listIndex(String key, long index) {
        return (T) this.opsList.index(key, index);
    }


    /**
     * 给集合key添加多个值，集合不存在创建后再添加
     * @param key
     * @param values
     * @return
     */
    public Long opsSetAdd(String key, Object... values) {
        return this.opsSet.add(key, values);
    }


    /**
     * 移除集合中多个value值
     * @param key
     * @param values
     * @return
     */
    public Long opsSetRemove(String key, Object... values) {
        return this.opsSet.remove(key, values);
    }


    /**
     * 随机删除集合中的一个值，并返回。
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T opsSetPop(String key) {
        return (T) this.opsSet.pop(key);
    }

    /**
     * 把源集合中的一个元素移动到目标集合。成功返回true.
     * @param key
     * @param value
     * @param destKey
     * @return
     */
    public Boolean opsSetMove(String key, Object value, String destKey) {
        return this.opsSet.move(key, value, destKey);
    }

    /**
     * 检查集合中是否包含某个元素
     * @param key
     * @param value
     * @return
     */
    public Boolean opsSetIsMember(String key, Object value) {
        return this.opsSet.isMember(key, value);
    }

    /**
     * 求指定集合与另一个集合的交集
     * @param key
     * @param otherKey
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> opsSetIntersect(String key, String otherKey) {
        return (Set<T>) this.opsSet.intersect(key, otherKey);
    }

    /**
     * 肾用！！（没搞明白otherKeys指的是否是redis中key set中的key集合）
     * 求指定集合与另一个集合的交集
     * @param key
     * @param otherKeys
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> opsSetIntersect(String key, Collection<String> otherKeys) {
        return (Set<T>) this.opsSet.intersect(key, otherKeys);
    }

    /**
     * 求指定集合与另一个集合的差集
     * @param key
     * @param otherKey
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> opsSetDifference(String key, String otherKey) {
        return (Set<T>) this.opsSet.difference(key, otherKey);
    }

    /**
     * 求指定集合与另一个集合的差集
     * 肾用！！（没搞明白otherKeys指的是否是redis中key set中的key集合）
     * @param key
     * @param otherKeys
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> opsSetDifference(String key, Collection<String> otherKeys) {
        return (Set<T>) this.opsSet.difference(key, otherKeys);
    }

    /**
     * 获取集合中的所有元素
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> opsSetMembers(String key) {
        return (Set<T>) this.opsSet.members(key);
    }

    /**
     * 随机获取集合中的一个元素
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T opsSetRandomMember(String key) {
        return (T) this.opsSet.randomMember(key);
    }

    /**
     * 随机返回集合中指定数量的元素。随机的元素不会重复
     * @param key
     * @param count
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> opsSetDistinctRandomMembers(String key, long count) {
        return (Set<T>) this.opsSet.distinctRandomMembers(key, count);
    }

    /**
     * 随机返回集合中指定数量的元素。随机的元素可能重复
     * @param key
     * @param count
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> opsSetRandomMembers(String key, long count) {
        return (Set<T>) this.opsSet.randomMembers(key, count);
    }


    /**
     * 判断是否存在key
     * @param key
     * @return
     */
    public boolean existsKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 重名名key，如果newKey已经存在，则newKey的原值被覆盖
     * @param oldKey
     * @param newKey
     */
    public void renameKey(String oldKey, String newKey) {
        redisTemplate.rename(oldKey, newKey);
    }

    /**
     * newKey不存在时才重命名
     * @param oldKey
     * @param newKey
     * @return 修改成功返回true
     */
    public boolean renameKeyNotExist(String oldKey, String newKey) {
        return redisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 删除key
     * @param key
     */
    public void deleteKey(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 删除多个key
     * @param keys
     */
    public void deleteKey(String... keys) {
        Set<String> kSet = Stream.of(keys).map(k -> k).collect(Collectors.toSet());
        redisTemplate.delete(kSet);
    }

    /**
     * 删除Key的集合
     * @param keys
     */
    public void deleteKey(Collection<String> keys) {
        Set<String> kSet = keys.stream().map(k -> k).collect(Collectors.toSet());
        redisTemplate.delete(kSet);
    }

    /**
     * 设置key的生命周期
     * @param key
     * @param time
     * @param timeUnit
     */
    public void expireKey(String key, long time, TimeUnit timeUnit) {
        redisTemplate.expire(key, time, timeUnit);
    }

    /**
     * 指定key在指定的日期过期
     * @param key
     * @param date
     */
    public void expireKeyAt(String key, Date date) {
        redisTemplate.expireAt(key, date);
    }

    /**
     * 查询key的生命周期
     * @param key
     * @param timeUnit
     * @return
     */
    public long getKeyExpire(String key, TimeUnit timeUnit) {
        return redisTemplate.getExpire(key, timeUnit);
    }

    /**
     * 将key设置为永久有效
     * @param key
     */
    public void persistKey(String key) {
        redisTemplate.persist(key);
    }

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public ValueOperations<String, Object> getOpsValue() {
        return opsValue;
    }

    public void setOpsValue(ValueOperations<String, Object> opsValue) {
        this.opsValue = opsValue;
    }

    public HashOperations<String, String, Object> getOpsHash() {
        return opsHash;
    }

    public void setOpsHash(HashOperations<String, String, Object> opsHash) {
        this.opsHash = opsHash;
    }

    public ListOperations<String, Object> getOpsList() {
        return opsList;
    }

    public void setOpsList(ListOperations<String, Object> opsList) {
        this.opsList = opsList;
    }

    public SetOperations<String, Object> getOpsSet() {
        return opsSet;
    }

    public void setOpsSet(SetOperations<String, Object> opsSet) {
        this.opsSet = opsSet;
    }

    public ZSetOperations<String, Object> getOpsZSet() {
        return opsZSet;
    }

    public void setOpsZSet(ZSetOperations<String, Object> opsZSet) {
        this.opsZSet = opsZSet;
    }
}